﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

//buddy
using Zeta;
using Zeta.Common;
using Zeta.CommonBot;
using Zeta.Common.Plugins;
using Zeta.Internals;
using Zeta.Internals.Actors;
using Zeta.Internals.Service;

using BossHog.ProfileBuilder;

namespace BossHog
{
    public partial class ProfileEditorTreeView : UserControl
    {
        string myNodePath = "";
        TreeNode myNode = null;
        ProfileElement Profile { get; set; }
        OrderElement Order { get; set; }
        TreeNode ProfileTreeNode { get; set; }
        TreeNode OrderTreeNode { get; set; }

        public ProfileEditorTreeView()
        {
            InitializeComponent();
        }

        private string filePath = "";

        bool IsValidXml(string xml)
        {
            bool ret = true;
            lock (this)
            {
                System.Xml.XmlTextReader r = null;
                System.IO.StringReader sr = null;
                try
                {
                    sr = new System.IO.StringReader(xml);
                    r = new System.Xml.XmlTextReader(sr);
                    while (r.Read()) { }
                }
                catch (Exception ex)
                {
                    Logging.Write("[BossHog] Error: " + ex.Message);
                    ret = false;
                }
                finally
                {
                    if (null != r)
                    {
                        r.Close();
                        r = null;
                    }

                    if (null != sr)
                    {
                        sr.Close();
                        sr = null;
                    }
                }
            }
            return ret;
        }

        void NewProfile()
        {
            filePath = "";
            profileCmdTree.Nodes.Clear();
            Profile = null;
            Order = null;
            ProfileTreeNode = null;
            OrderTreeNode = null;
            ParseProfile("<Profile><Name>New Profile</Name><GameParams /><Order></Order><KillMonsters>True</KillMonsters><PickupLoot>True</PickupLoot></Profile>");
        }

        void ParseProfile(string xml)
        {
            if (!IsValidXml(xml)) return;

            System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
            doc.LoadXml(xml);

            Logging.Write(doc.FirstChild.Name);
            if (doc.FirstChild.Name == "Profile")
            {
                profileCmdTree.Nodes.Clear();

                Profile = null;
                Order = null;

                ProfileTreeNode = null;
                OrderTreeNode = null;

                ProfileTreeNode = new TreeNode("<Profile>");
                Profile = new ProfileElement();
                ProfileTreeNode.Tag = Profile; //create and link

                profileCmdTree.Nodes.Add(ProfileTreeNode);

                if (null != doc.FirstChild.ChildNodes && doc.FirstChild.ChildNodes.Count > 0)
                {
                    ParseProfileChildren(doc.FirstChild.ChildNodes);
                }

                UpdateProfile(); //refresh view
                if (null != ProfileTreeNode)
                {
                    ProfileTreeNode.ExpandAll();
                    myNode = GetNodeFromPath(ProfileTreeNode, myNodePath);
                    if (null != myNode && null != myNode.Tag)
                    {
                        profileCommandProperties.SelectedObject = myNode.Tag;
                    }
                }

            }
        }

        public TreeNode GetNodeFromPath(TreeNode node, string path)
        {
            TreeNode foundNode = null;
            foreach (TreeNode tn in node.Nodes)
            {
                if (tn.FullPath == path)
                {
                    return tn;
                }
                else if (tn.Nodes.Count > 0)
                {
                    foundNode = GetNodeFromPath(tn, path);
                }
                if (foundNode != null)
                    return foundNode;
            }
            return null;
        }

        void ParseProfileChildren(System.Xml.XmlNodeList nodes)
        {
            if (null != nodes && nodes.Count > 0)
            {
                for (int i = 0; i < nodes.Count; i++)
                {
                    if (nodes.Count > i && null != nodes[i])
                    {
                        switch (nodes[i].Name)
                        {
                            case "Name":
                                NameElement nm = new NameElement();
                                TreeNode nmn = new TreeNode("<Name>");
                                nm.name = nodes[i].InnerXml;
                                nmn.Tag = nm;
                                Profile.Commands.Add(nm);
                                ProfileTreeNode.Nodes.Add(nmn);
                                nmn.Text = nm.ToString();
                                break;
                            case "GameParams":
                                GameParamsElement gp = new GameParamsElement();
                                TreeNode gpn = new TreeNode("<GameParams>");
                                if (null != nodes[i].Attributes["quest"]) gp.quest = nodes[i].Attributes["quest"].Value;
                                if (null != nodes[i].Attributes["step"]) gp.step = nodes[i].Attributes["step"].Value;
                                if (null != nodes[i].Attributes["resumeFromSave"]) gp.resumeFromSave = nodes[i].Attributes["resumeFromSave"].Value;
                                if (null != nodes[i].Attributes["numGames"]) gp.numGames = nodes[i].Attributes["numGames"].Value;
                                if (null != nodes[i].Attributes["isPrivate"]) gp.isPrivate = nodes[i].Attributes["isPrivate"].Value;
                                if (null != nodes[i].Attributes["difficulty"]) gp.difficulty = nodes[i].Attributes["difficulty"].Value;
                                if (null != nodes[i].Attributes["act"]) gp.act = nodes[i].Attributes["act"].Value;

                                gpn.Tag = gp;
                                Profile.Commands.Add(gp);
                                ProfileTreeNode.Nodes.Add(gpn);
                                gpn.Text = gp.ToString();
                                break;
                            case "Order":
                                Order = new OrderElement();
                                OrderTreeNode = new TreeNode("<Order>");
                                OrderTreeNode.Tag = Order;

                                if (null != nodes[i].ChildNodes && nodes[i].ChildNodes.Count > 0)
                                {
                                    ParseInstructionSetChildren(nodes[i].ChildNodes, OrderTreeNode, Order);
                                }

                                Profile.Commands.Add(Order);
                                ProfileTreeNode.Nodes.Add(OrderTreeNode);
                                OrderTreeNode.Text = Order.ToString();
                                break;
                            case "KillMonsters":
                                KillMonstersElement km = new KillMonstersElement();
                                TreeNode kmn = new TreeNode("<KillMonsters>");
                                kmn.Tag = km;
                                km.value = nodes[i].InnerXml;
                                Profile.Commands.Add(km);
                                ProfileTreeNode.Nodes.Add(kmn);
                                kmn.Text = km.ToString();
                                break;
                            case "PickupLoot":
                                PickupLootElement pl = new PickupLootElement();
                                TreeNode pln = new TreeNode("<PickupLoot>");
                                pl.value = nodes[i].InnerXml;
                                pln.Tag = pl;
                                Profile.Commands.Add(pl);
                                ProfileTreeNode.Nodes.Add(pln);
                                pln.Text = pl.ToString();
                                break;
                        }
                    }
                }
            }
        }

        void ParseInstructionSetChildren(System.Xml.XmlNodeList nodes, TreeNode p, ICommand el)
        {
            if (null != nodes && nodes.Count > 0)
            {
                for (int i = 0; i < nodes.Count; i++)
                {
                    if (nodes.Count > i && null != nodes[i])
                    {
                        switch (nodes[i].Name)
                        {
                            case "If":
                                IfElement ify = new IfElement();
                                ify.condition = nodes[i].Attributes["condition"].Value;
                                TreeNode ifyn = new TreeNode("<If condition=\"" + ify.condition + "\">");
                                ifyn.Tag = ify;
                                el.Commands.Add(ify);
                                p.Nodes.Add(ifyn);
                                if (null != nodes[i].ChildNodes && nodes[i].ChildNodes.Count > 0)
                                {
                                    ParseInstructionSetChildren(nodes[i].ChildNodes, ifyn, ify);
                                }
                                break;
                            case "While":
                                WhileElement whiley = new WhileElement();
                                whiley.condition = nodes[i].Attributes["condition"].Value;
                                TreeNode whileyn = new TreeNode("<While condition=\"" + whiley.condition + "\">");
                                whileyn.Tag = whiley;
                                el.Commands.Add(whiley);
                                p.Nodes.Add(whileyn);
                                if (null != nodes[i].ChildNodes && nodes[i].ChildNodes.Count > 0)
                                {
                                    ParseInstructionSetChildren(nodes[i].ChildNodes, whileyn, whiley);
                                }
                                break;
                            default:
                                Type t = System.Reflection.Assembly.GetExecutingAssembly().GetType("BossHog.ProfileBuilder." + nodes[i].Name + "Element");
                                if (null != t)
                                {
                                    System.Reflection.ConstructorInfo ci = t.GetConstructor(Type.EmptyTypes);
                                    System.Reflection.PropertyInfo[] pi = t.GetProperties();
                                    System.Reflection.MethodInfo toString = t.GetMethod("ToString");

                                    if (null != ci && null != pi && pi.Length > 0)
                                    {
                                        object o = ci.Invoke(null);
                                        if (null != o)
                                        {
                                            for (int j = 0; j < pi.Length; j++)
                                            {
                                                if (null != nodes[i].Attributes[pi[j].Name]) pi[j].SetValue(o, nodes[i].Attributes[pi[j].Name].Value, null);
                                            }

                                            TreeNode n = new TreeNode(Convert.ToString(toString.Invoke(o,new object[]{})));

                                            n.Tag = o;
                                            el.Commands.Add((IOrderChildElement)o);
                                            p.Nodes.Add(n);
                                        }
                                    }
                                }
                                break;
                        }
                    }
                }
            }
        }


        void AddOrder(string c, IOrderChildElement t)
        {
            if (null == myNode) return;

            lock (this)
            {
                TreeNode n = new TreeNode(c);
                n.Tag = t;
                myNode.Nodes.Add(n);
                if (null != myNode.Tag)
                {
                    if (null == ((ICommand)myNode.Tag).Commands)
                        ((ICommand)myNode.Tag).Commands = new List<IOrderChildElement>();

                    ((ICommand)myNode.Tag).Commands.Add(t);
                }
            }

            ParseProfile(Profile.ToString());
        }

        void UpdateProfile()
        {
            lock (this)
            {
                if (null == Profile) return;

                string xml = "";
                System.Text.StringBuilder sb = new StringBuilder();
                System.IO.StringWriter sr = new System.IO.StringWriter(sb);
                System.Xml.XmlTextWriter w = new System.Xml.XmlTextWriter(sr);

                //setup formating
                w.Indentation = 4;
                w.IndentChar = ' ';
                w.Formatting = System.Xml.Formatting.Indented;

                //write
                Profile.Write(w);
                xml = sb.ToString();

                //clean
                w.Close();
                sr.Close();
                sb.Clear();

                //output
                profileXmlTxt.Text = xml;
                profileXmlTxt.FormatXml();
            }
        }

        void Open()
        {
            System.IO.Stream s = null;
            System.IO.StreamReader sr = null;

            try
            {
                s = System.IO.File.Open(filePath, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite, System.IO.FileShare.Read);
                sr = new System.IO.StreamReader(s);
                string xml = sr.ReadToEnd();
                ParseProfile(xml);
            }
            catch (Exception ex)
            {
                Logging.Write("[BossHog] Error: " + ex.Message);
            }
            finally
            {
                if (null != sr)
                {
                    sr.Close();
                    sr.Dispose();
                    sr = null;
                }

                if (null != s)
                {
                    s.Close();
                    s.Dispose();
                    s = null;
                }
            }
        }

        void Save()
        {
            System.IO.Stream s = null;
            System.IO.StreamWriter w = null;

            try
            {
                try
                {
                    if (System.IO.File.Exists(filePath))
                    {
                        System.IO.File.Delete(filePath);
                    }
                }
                catch (Exception failed) { Logging.Write(LogLevel.Verbose, "[BossHog] Error: " + failed.Message); }

                s = System.IO.File.Open(filePath, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write, System.IO.FileShare.Read);
                w = new System.IO.StreamWriter(s);
                lock (this)
                {
                    w.Write(profileXmlTxt.Text);
                }

            }
            catch (Exception ex)
            {
                Logging.Write("[BossHog] Error: " + ex.Message);
            }
            finally
            {
                if (null != w)
                {
                    w.Close();
                    w.Dispose();
                    w = null;
                }

                if (null != s)
                {
                    s.Close();
                    s.Dispose();
                    s = null;
                }
            }
        }

        void Run()
        {
            BotMain.Stop();
            string fp = filePath;
            filePath = Environment.GetFolderPath(Environment.SpecialFolder.CommonDocuments) + "\\BWTmp.xml";
            Save();
            Zeta.CommonBot.ProfileManager.Load(Environment.GetFolderPath(Environment.SpecialFolder.CommonDocuments) + "\\BWTmp.xml");
            filePath = fp;
            BotMain.Start();
        }

        #region Order Menu
        private void useTownPortalClick(object sender, EventArgs e)
        {
            AddOrder("<UseTownPortal>", new UseTownPortalElement());
        }

        private void useObjectToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            AddOrder("<UseObject>", new UseObjectElement());
        }

        private void talkToClick(object sender, EventArgs e)
        {
            AddOrder("<TalkTo>", new TalkToElement());
        }

        private void usePowerClick(object sender, EventArgs e)
        {
            AddOrder("<UsePower>", new UsePowerElement());
        }

        private void useWaypointToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddOrder("<UseWaypoint>", new UseWaypointElement());
        }

        private void moveToClick(object sender, EventArgs e)
        {
            AddOrder("<MoveTo>", new MoveToElement());
        }

        private void waitClick(object sender, EventArgs e)
        {
            AddOrder("<Wait>", new WaitTimerElement());
        }

        private void skipCutsceneClick(object sender, EventArgs e)
        {
            AddOrder("<SkipCutscene>", new SkipCutsceneElement());
        }

        private void leaveGameClick(object sender, EventArgs e)
        {
            AddOrder("<LeaveGame>", new LeaveGameElement());
        }

        private void writeReportClick(object sender, EventArgs e)
        {
            AddOrder("<WriteReport>", new WriteReportElement());
        }

        private void ifClick(object sender, EventArgs e)
        {
            AddOrder("<If>", new IfElement());
        }

        private void whileClick(object sender, EventArgs e)
        {
            AddOrder("<While>", new WhileElement());
        }

        #endregion
        #region Misc Events
        private void profileCommandProperties_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            ParseProfile(Profile.ToString());
        }

        private void profileCmdTree_NodeMouseClick(object sender, System.Windows.Forms.TreeNodeMouseClickEventArgs e)
        {
            if (null == e.Node) return;

            myNode = e.Node;
            if (null != myNode)
            {
                try
                {
                    myNodePath = myNode.FullPath;
                }
                catch (Exception ex) { Logging.Write(LogLevel.Verbose, "[BossHog] Error: " + ex.Message); /* sometimes it gets a node that is not in a tree, not important, just causes an error here */ }
            }

            if (null != e.Node.Tag)
            {
                profileCommandProperties.SelectedObject = e.Node.Tag;
            }

            for (int i = 0; i < profileToolbar.Items.Count; i++)
            {
                profileToolbar.Items[i].Visible = false;
            }

            newProfileBtn.Visible = true;
            openProfileBtn.Visible = true;
            saveProfileBtn.Visible = true;

            //Logging.Write("[BossHog] " + myNode.Tag.GetType().ToString());

            if (null != myNode && null != myNode.Tag)
            {
                switch (myNode.Tag.GetType().ToString())
                {
                    case "BossHog.ProfileBuilder.ProfileElement":
                        break;
                    case "BossHog.ProfileBuilder.OrderElement":
                    case "BossHog.ProfileBuilder.IfElement":
                    case "BossHog.ProfileBuilder.WhileElement":
                        instructionSetMnu.Visible = true;
                        break;
                    default:
                        removeNodeBtn.Visible = true;
                        break;
                }
            }
        }

        private void removeNodeBtn_Click(object sender, EventArgs e)
        {
            if (null != myNode)
            {
                if (null != myNode.Parent)
                {
                    if (myNode.Parent.Tag is IfElement)
                    {
                        ((IfElement)myNode.Parent.Tag).Commands.Remove((IOrderChildElement)myNode.Tag);
                    }

                    if (myNode.Parent.Tag is WhileElement)
                    {
                        ((WhileElement)myNode.Parent.Tag).Commands.Remove((IOrderChildElement)myNode.Tag);
                    }


                    if (myNode.Parent.Tag is ProfileElement)
                    {
                        ((ProfileElement)myNode.Parent.Tag).Commands.Remove((IProfileChildElement)myNode.Tag);
                    }

                    if (myNode.Parent.Tag is OrderElement)
                    {
                        ((OrderElement)myNode.Parent.Tag).Commands.Remove((IOrderChildElement)myNode.Tag);
                    }

                    myNode.Parent.Nodes.Remove(myNode);
                }
            }
        }
        private void saveProfileBtn_Click(object sender, EventArgs e)
        {
            if (filePath == "")
            {
                DialogResult r = saveProfileDlg.ShowDialog();
                if (r == System.Windows.Forms.DialogResult.OK)
                {
                    filePath = saveProfileDlg.FileName;
                    Save();
                }
            }
            else
            {
                Save();
            }
        }
        private void openProfileBtn_Click(object sender, EventArgs e)
        {
            DialogResult r = openProfileDlg.ShowDialog();
            if (r == System.Windows.Forms.DialogResult.OK)
            {
                if (System.IO.File.Exists(openProfileDlg.FileName))
                {
                    filePath = openProfileDlg.FileName;
                    Open();
                }
            }
        }

        private void newProfileBtn_Click(object sender, EventArgs e)
        {
            NewProfile();
        }

        #endregion
        #region UseObject Menus
        void BindUseObjectMenus()
        {
            useObjectToolStripMenuItem1.DropDownItems.Clear();
            talkToToolStripMenuItem.DropDownItems.Clear();
            useWaypointToolStripMenuItem.DropDownItems.Clear();

            IEnumerable<ACD> acds = ZetaDia.Actors.GetActorsOfType<ACD>();
            IEnumerator<ACD> ieAcd = acds.GetEnumerator();

            while (ieAcd.MoveNext())
            {
                try { AddUseWpMenuItem(ieAcd.Current); }
                catch (Exception ex) { Logging.Write(LogLevel.Verbose, "[BossHog] Error: " + ex.Message); }
                try { AddTalkToMenuItem(ieAcd.Current); }
                catch (Exception ex) { Logging.Write(LogLevel.Verbose, "[BossHog] Error: " + ex.Message); }
                try { AddObjectMenuItem(ieAcd.Current); }
                catch (Exception ex) { Logging.Write(LogLevel.Verbose, "[BossHog] Error: " + ex.Message); }
            }
        }

        void AddObjectMenuItem(ACD acd)
        {
            ToolStripMenuItem item = new ToolStripMenuItem();
            item.Text = acd.Name + ":" + acd.ActorSNO.ToString();
            item.Click += new EventHandler(objectItem_Click);
            item.Tag = acd;
            useObjectToolStripMenuItem1.DropDownItems.Add(item);
        }

        void AddTalkToMenuItem(ACD acd)
        {
            ToolStripMenuItem item = new ToolStripMenuItem();
            item.Text = acd.Name + ":" + acd.ActorSNO.ToString();
            item.Click += new EventHandler(talkToItem_Click);
            item.Tag = acd;
            talkToToolStripMenuItem.DropDownItems.Add(item);
        }

        void AddUseWpMenuItem(ACD acd)
        {
            ToolStripMenuItem item = new ToolStripMenuItem();
            item.Text = acd.Name + ":" + acd.ActorSNO.ToString();
            item.Click += new EventHandler(useWpItem_Click);
            item.Tag = acd;
            useWaypointToolStripMenuItem.DropDownItems.Add(item);
        }

        void objectItem_Click(object sender, EventArgs e)
        {
            UseObjectElement p = new UseObjectElement();
            ACD acd = (ACD)((ToolStripMenuItem)sender).Tag;
            p.questId = ZetaDia.CurrentQuest.QuestSNO.ToString();
            p.actorId = acd.ActorSNO.ToString();
            p.x = acd.Position.X.ToString();
            p.y = acd.Position.Y.ToString();
            p.z = acd.Position.Z.ToString();
            p.isPortal = "False";
            p.destinationWorldId = "-1";
            AddOrder("<UseObject>", p);
        }

        void talkToItem_Click(object sender, EventArgs e)
        {
            TalkToElement p = new TalkToElement();
            ACD acd = (ACD)((ToolStripMenuItem)sender).Tag;
            p.questId = ZetaDia.CurrentQuest.QuestSNO.ToString();
            p.actorId = acd.ActorSNO.ToString();
            p.x = acd.Position.X.ToString();
            p.y = acd.Position.Y.ToString();
            p.z = acd.Position.Z.ToString();
            p.stepId = ZetaDia.CurrentQuest.StepId.ToString();
            AddOrder("<TalkTo>", p);
        }

        void useWpItem_Click(object sender, EventArgs e)
        {
            UseWaypointElement p = new UseWaypointElement();
            ACD acd = (ACD)((ToolStripMenuItem)sender).Tag;
            p.questId = ZetaDia.CurrentQuest.QuestSNO.ToString();
            p.actorId = acd.ActorSNO.ToString();
            p.x = acd.Position.X.ToString();
            p.y = acd.Position.Y.ToString();
            p.z = acd.Position.Z.ToString();
            p.waypointNumber = "-1";
            AddOrder("<UseWaypoint>", p);
        }
        #endregion

        private void useObjectToolStripMenuItem_MouseHover(object sender, EventArgs e)
        {
            BindUseObjectMenus();
        }

        private void profileXmlTxt_KeyUp(object sender, KeyEventArgs e)
        {
            profileXmlTxt.SaveCaret();
            ParseProfile(profileXmlTxt.Text);
            profileXmlTxt.RestoreCaret();
        }

        private void ProfileEditorTreeView_SizeChanged(object sender, EventArgs e)
        {

        }



    }
}
